/*
 * Android Runner is a multiplayer GPS game fully written by Xurxo Mendez Perez
 * 
 * Copyright (C) 2009 Xurxo Mendez Perez
 *   
 * This file is part of Android Runner.
 * 
 * Android Runner is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Android Runner is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with Android Runner.  If not, see <http://www.gnu.org/licenses/>.
 */

package es.sonxurxo.androidrunner.model.persistence.game.accessor;

import java.util.Calendar;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;

import es.sonxurxo.androidrunner.model.persistence.game.cto.GameCTO;
import es.sonxurxo.androidrunner.model.persistence.game.entity.Game;
import es.sonxurxo.androidrunner.model.persistence.util.accessor.GenericAccessorHibernate;

/**
 * 
 * @author "Xurxo Mendez Perez"
 *
 */
public class GameAccessorHibernate 
	extends GenericAccessorHibernate<Game, Long> 
	implements GameAccessor {
	
	private static final int LIMIT_GAME_DISTANCE = 2;

	@SuppressWarnings("unchecked")
	public GameCTO findNotFinishedByCity(String city, int startIndex,
			int count) {
		Calendar currentTime = Calendar.getInstance();
		Criteria criteria = getSession().createCriteria(Game.class);
		criteria.addOrder(Order.asc("finishDate"));
		criteria.setFirstResult(startIndex);
		criteria.setMaxResults(count + 1);
		criteria.add(Restrictions.ge("finishDate", currentTime));
		criteria.add(Restrictions.ne("finished", true));
		criteria.add(Restrictions.ilike("city", ("%" + city + "%")));
		List resultList = criteria.list();
		
		boolean hasMore = false;
		if (resultList.size() == count + 1) {
			resultList = resultList.subList(0, resultList.size() - 1);
			hasMore = true;
		}
		return new GameCTO(resultList, hasMore);
	}
	
	@SuppressWarnings("unchecked")
	public GameCTO findNotFinishedByLocation(int latitude, int longitude,
			int accurate, int startIndex, int count) {
		Calendar currentTime = Calendar.getInstance();

		int accurateParameter = accurate;
		if (accurateParameter == 0)
			accurateParameter = LIMIT_GAME_DISTANCE;
		int latitudeMargin = latitude + accurate;
		int longitudeMargin = longitude + accurate;
		
		Criteria criteria = getSession().createCriteria(Game.class);
		criteria.addOrder(Order.asc("city"));
		criteria.setFirstResult(startIndex);
		criteria.setMaxResults(count);
		criteria.add(Restrictions.ge("finishDate", currentTime));
		criteria.add(Restrictions.ne("finished", true));
		criteria.add(Restrictions.lt("latitude", latitudeMargin));
		criteria.add(Restrictions.lt("longitude", longitudeMargin));
		List resultList = criteria.list();
		
		boolean hasMore = false;
		if (resultList.size() == count + 1) {
			resultList = resultList.subList(0, resultList.size() - 1);
			hasMore = true;
		}
		return new GameCTO(resultList, hasMore);
	}

	@SuppressWarnings("unchecked")
	public List<String> findCitiesWithGames() {
		Calendar currentTime = Calendar.getInstance();
		currentTime.add(Calendar.HOUR_OF_DAY, 1);
		
		return getSession().createQuery("SELECT DISTINCT g.city FROM Game g " + 
				"WHERE g.finishDate > :currentTime " + 
				"ORDER BY g.city").setTimestamp("currentTime", currentTime.getTime()).list();
	}

	@SuppressWarnings("unchecked")
	public GameCTO findActiveGames(int startIndex, int count) {
		Calendar currentTime = Calendar.getInstance();
		currentTime.add(Calendar.HOUR_OF_DAY, 1);
		
		Criteria criteria = getSession().createCriteria(Game.class);

		criteria.addOrder(Order.asc("city"));
		criteria.setFirstResult(startIndex);
		criteria.setMaxResults(count);
		criteria.add(Restrictions.le("startDate", currentTime));
		criteria.add(Restrictions.ne("finished", new Boolean(true)));
		List resultList = criteria.list();
		boolean hasMore = false;
		if (resultList.size() == count + 1) {
			resultList = resultList.subList(0, resultList.size() - 1);
			hasMore = true;
		}
		return new GameCTO(resultList, hasMore);
	}

	public Integer countActiveGames() {
		Calendar currentTime = Calendar.getInstance();
		currentTime.add(Calendar.HOUR_OF_DAY, 1);
		
		Criteria criteria = getSession().createCriteria(Game.class);

		criteria.addOrder(Order.asc("city"));
		criteria.add(Restrictions.le("startDate", currentTime));
		criteria.add(Restrictions.ne("finished", new Boolean(true)));
		return new Integer(criteria.list().size());
	}

	@SuppressWarnings("unchecked")
	public GameCTO findNotFinishedGames(int startIndex, int count) {
		
		Criteria criteria = getSession().createCriteria(Game.class);
		criteria.addOrder(Order.asc("city"));
		criteria.setFirstResult(startIndex);
		criteria.setMaxResults(count);
		criteria.add(Restrictions.ne("finished", true));
		List resultList = criteria.list();
		
		boolean hasMore = false;
		if (resultList.size() == count + 1) {
			resultList = resultList.subList(0, resultList.size() - 1);
			hasMore = true;
		}
		return new GameCTO(resultList, hasMore);
	}
	
	public Integer countNotFinishedGames() {
		
		Criteria criteria = getSession().createCriteria(Game.class);
		criteria.addOrder(Order.asc("city"));
		criteria.add(Restrictions.ne("finished", true));
		return new Integer(criteria.list().size());
	}

	@SuppressWarnings("unchecked")
	public List<Game> findFinishedGames(int startIndex, int count) {
		Criteria criteria = getSession().createCriteria(Game.class);
		criteria.addOrder(Order.asc("city"));
		criteria.add(Restrictions.eq("finished", true));
		return criteria.list();
	}
	
	public Integer countFinishedGames() {
		
		Criteria criteria = getSession().createCriteria(Game.class);
		criteria.addOrder(Order.asc("city"));
		criteria.add(Restrictions.eq("finished", true));
		return new Integer(criteria.list().size());
	}
}
